package ua.kpi.ipsa.expert.api.algorithm;

import java.util.*;

import ua.kpi.ipsa.expert.api.algorithm.tokenizer.*;
import ua.kpi.ipsa.expert.api.db.*;

import ua.kpi.ipsa.expert.api.algorithm.tokenizer.Tokenizer.Token;

/**
 * 
 * @author doctorpro
 * 
 */
public class HaskellAlgo implements Algorithm {

	private static final long PRIMAL = 37;

	private static final double LOWER_TRESHOLD = 0.2;
	private static final double UPPER_TRESHOLD = 0.8;

	private long pows[] = new long[1000];
	private DAO dao;

	public HaskellAlgo(DAO dao) {
		this.dao = dao;
	}

	@Override
	public List<PlagiarismHypoTuple> process(int codeToAnalizeId,
			CodeRanker ranker, ProgressMonitor monitor) {

		ArrayList<PlagiarismHypoTuple> gippos = new ArrayList<PlagiarismHypoTuple>();

		try {
			int myCodePortionId = dao.getCode(codeToAnalizeId).codePortionTupleFK;
			int id = 0;
			//
			Tokenizer t = new JavaTokenizer();
			Token[] myTokens = t.parse(dao.getCode(codeToAnalizeId).text);
			//
			while ((id = dao.getNextCodePortionID(id)) < myCodePortionId) {
				List<CodeTuple> cts = dao.getCodeByPortionKey(id);
				for (CodeTuple ct : cts) {
					// тут обработка каждого кода
					//
					Token[] guestTokens = t.parse(ct.text);
					//
					int length = getCommonLength(myTokens, guestTokens);
					double ratio = ((double) length)
							/ min(myTokens.length, guestTokens.length);
					double gippoRatio = 0;
					if (ratio < LOWER_TRESHOLD) {
						gippoRatio = 0;
					} else if (ratio > UPPER_TRESHOLD) {
						gippoRatio = 1;
					} else {
						gippoRatio = (ratio - LOWER_TRESHOLD)
								/ (UPPER_TRESHOLD - LOWER_TRESHOLD);
					}

					PlagiarismHypoTuple pht = new PlagiarismHypoTuple();
					pht.code1FK = codeToAnalizeId;
					pht.code2FK = ct.codePK;
					pht.veracity = gippoRatio;

					gippos.add(pht);
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return gippos;
	}

	/**
	 * 
	 * @param s1
	 * @param s2
	 * @return
	 */
	public int getCommonLength(Token s1[], Token s2[]) {

		int length = 1;
		int coords[] = new int[2];
		
		while (getSubstring(s1, s2, coords, length)) {
			length++;
		}

		return length;
	}

	/**
	 * 
	 * @param a
	 * @param b
	 * @return
	 */
	public int min(int a, int b) {
		return a <= b ? a : b;
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public int[] getTokens2(String s) {

		int myTokens[] = new int[] { 2, 7, 6, 31, 31, 31, 5, 6, 31, 2, 32, 32,
				1, 5 };

		return myTokens;
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public int[] getTokens(String s) {

		int myTokens[] = new int[] { 1, 2, 3, 4, 7, 6, 31, 31, 31, 5, 6, 31, 2,
				32, 44, 44, 12, 3123 };

		return myTokens;
	}

	/**
	 * 
	 * @param s1
	 * @param s2
	 * @param coords
	 * @param substringLength
	 * @return
	 */
	public boolean getSubstring(Token s1[], Token s2[], int coords[],
			int substringLength) {

		// create set
		long[] set = new long[s1.length - substringLength + 1];
		for (int i = 0; i + substringLength - 1 < s1.length; i++) {
			set[i] = hash(s1, i, i + substringLength - 1);
		}

		// check hash in set
		for (int i = 0; i + substringLength - 1 < s2.length; i++) {

			int j = contains(set, hash(s2, i, i + substringLength - 1));

			if (j != -1) {
				if (equals(s1, s2, j, i, substringLength)) {
					coords[0] = j;
					coords[1] = i;
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * 
	 * @param s
	 * @param begin
	 * @param end
	 * @return
	 */
	public long hash(Token s[], int begin, int end) {

		if (begin > end) {
			int t = end;
			end = begin;
			begin = t;
		}

		long result = 0;

		for (int i = begin, c = end - begin; i <= end; i++, c--) {
			result += s[i].getID() * pow(c);
		}

		return result;
	}

	/**
	 * 
	 * @param set
	 * @param ex
	 * @return
	 */
	public int contains(long[] set, long ex) {
		for (int i = 0; i < set.length; i++) {
			if (set[i] == ex) {
				return i;
			}
		}

		return -1;
	}

	/**
	 * 
	 * @param s1
	 * @param s2
	 * @param begin1
	 * @param begin2
	 * @param length
	 * @return
	 */
	public boolean equals(Token s1[], Token s2[], int begin1, int begin2,
			int length) {

		for (int i = 0; i < length; i++) {
			if (s1[begin1 + i].getID() != s2[begin2 + i].getID()) {
				return false;
			}
		}

		return true;
	}

	/**
	 * 
	 * @param pow
	 * @return
	 */
	public long pow(int pow) {

		if (pow > pows.length) {
			pows = new long[2 * pow];
		}

		if (pows[pow] == 0) {
			pows[pow] = (long) Math.pow(PRIMAL, pow);
		}

		return pows[pow];
	}

	@Override
	public String getName() {
		return "Haskell";
	}

	@Override
	public String[] getCodePresentationClasses() {
		return new String[] {};
	}

}
